package persistent.prestige.console.threadmode.t3;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

import persistent.prestige.console.threadmode.t3.bus.ServerEnv;

public class IoHandler {
	
	private static final int MAX_READ_SPIN_COUNT = 16; //一个通道读事件，最大允许的读次数
	private static final int MAX_WRITE_SPIN_COUNT = 16; //一个通道事件，最大运行的写次数
	private static final int RECV_BUFF_SIZE = 1024;
	
	/**
	 * 写事件处理
	 * @param key
	 * @param msg
	 */
	public void write(SelectionKey key, Object msg) {
		
		SocketChannel sc = (SocketChannel) key.channel();
		ByteBuffer buf = (ByteBuffer) key.attachment();
		
		if(buf == null || msg == null ) {
			//取消写事件
			clearOpWrite(key);
			return;
		}
		ByteBuffer[] bufs = null;
		ByteBuffer msgBuf = null;
		int bufCount;
		
		
		long expectWriteCount;
		if(buf != null && msg != null) {
			bufs = new ByteBuffer[2];
			msgBuf = (ByteBuffer)msg;
			bufs[0] = buf;
			bufs[1] = msgBuf;
			bufCount = 2;
			
			msgBuf.flip();
			expectWriteCount = buf.remaining() + msgBuf.remaining();
		} else {
			msgBuf = buf == null ? (ByteBuffer)msg : buf;
			bufCount = 1;
			expectWriteCount = msgBuf.remaining();
		}
		
		long localWrite = 0;
		
//		long totalWrite = 0;
		
		try {
			for(int i =0; i < MAX_WRITE_SPIN_COUNT; i ++ ) {
				
				if(bufCount == 1 ) {
					localWrite = sc.write(msgBuf);
//					totalWrite += localWrite;
					expectWriteCount -= localWrite;
					
					if(expectWriteCount == 0 ) {
						//表示在允许的循环内完成写操作，取消写事件
						clearOpWrite(key);
						break;
					}
					
					
					
				} else { //多个buf,
					localWrite = sc.write(bufs, 0, bufCount);
					expectWriteCount -= localWrite;
					
					if(expectWriteCount == 0 ) {
						//表示在允许的循环内完成写操作，取消写事件
						clearOpWrite(key);
						break;
					}
				}
			}
			
			if(expectWriteCount > 0 ) { //经过最大循环次数，数据未写完，结束本次写入，下次再写
				if(bufCount > 1 ) {
					if(bufs[0].hasRemaining()) {
						ByteBuffer remainingBuf = ByteBuffer.allocate( bufs[0].remaining() + msgBuf.remaining() );
						remainingBuf.put(bufs[0].array());
						remainingBuf.put(msgBuf.array());
						remainingBuf.flip();
						key.attach(remainingBuf);
						
						bufs = null;
						msgBuf = null;
						buf = null; // help GC
						
					}
				} else {
					key.attach(msgBuf);//放入key中的数据，已经是可读状态的，此处，个人觉得没有必要compact(),
					                  // 因为该ByteBuf不会继续写入，只会读取，并且compact方法，并不能节省内存，只是将已读数据从移除，但占用的内存并不会释放。
					
				}
			}
			
			
		} catch (IOException e) {
			e.printStackTrace();
			
		} finally {
			//在这里重新关注一下读事件
			addOpRead(key);  //这里我觉得没有必要，读事件的取消，是因为通知我读，但又没有数据可读，属于异常情况，没必要增加读事件，影响效率，你们觉得呢？这里还是为了保持健壮性，加上吧
		}
		
	
		
	}

	public void read(SelectionKey key) {
		
		//这里有几个地方可以优化：
		// 1、内存的分配使用内存池、线程本地缓存分配，因为这里是IO线程，线程本地分配具有非常大的优化表现，1、减少内存分配锁竞争2、可重复利用率高。
		// 2、这里在接受通道中可读数据时，该分配多大的空间呢？可以参考netty的实现，实现智能动态变化，此处使用固定大小的字节。
		int readCount;
		SocketChannel sc = (SocketChannel)key.channel();
		try {
			for(int i = 0; i > MAX_READ_SPIN_COUNT ;i ++ ) {
				ByteBuffer recvBuf = ByteBuffer.allocate(RECV_BUFF_SIZE);
				readCount = sc.read(recvBuf);
				
				if( readCount <= 0 ) { //如果读到字节为0，则需要取消读事件
					if(readCount < 0 ) {
						if(sc.isOpen()) {
							sc.close();
						}
						
					} else { //读取到0个字节，取消读事件
						clearOpRead(key);
					}
					
					break;
				}
				
				ServerEnv.pipeline().fireChannelRead(recvBuf);//触发读事件处理
				recvBuf = null;//help GC
				
			}
		} catch (IOException e) {
			e.printStackTrace();
			ServerEnv.pipeline().fireExceptionCaught(e);
		} finally {
			addOpWrite(key); // 这里我还是觉得没有必要，因为取消关注 写事件，是因为事件到达了，然后写入0字节，这应该是异常情况吧？
		}
		
	}
	
	
	protected void addOpRead(SelectionKey key) {
		key.interestOps(  key.interestOps() | SelectionKey.OP_READ  );
	}
	
	protected void clearOpRead(SelectionKey key) {
		key.interestOps(  key.interestOps() & ~SelectionKey.OP_READ  );
	}
	
	protected void clearOpWrite(SelectionKey key) {
		key.interestOps(  key.interestOps() & ~SelectionKey.OP_WRITE  );
	}
	
	protected void addOpWrite(SelectionKey key) {
		key.interestOps(  key.interestOps() | SelectionKey.OP_WRITE  );
	}

}
